# 41. hashlib模块 - 加密算法模块(摘要算法)

# hashlib摘要模块

hashlib模块提供了常用的摘要算法,比如MD5、SHA1等

什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)

摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同

对于同一个字符串,不管 这个字符串有多少,只要是相同的,无论在任何环境下,多少次执行,无论任何语言中,使用相同算法得到的结果永远都是相同的

**注意:**字符串可以通过算法加密成密文,但是密文不可以逆推成字符串

# hashlib模块的使用

目前先介绍二个算法:MD5、SHA1

# MD5算法

  1. 32位的算法程序,每个字符都是一个十六进制
  2. MD5算法效率多,算法相对简单
  3. 目前大量的人使用MD5算法,也导致MD5的撞库的数据库比较多,比较容易让人破解
    1. 撞库:就是别人把值跟对应的MD5存在一个数据库里,可以拿一个MD5值去数据进行对比,如果存在,在把数据库中的MD5对应的值拿出来,这就相对的破解了
import hashlib
so = "sakjkh21h3h12jkh3jk1h23jk1"
md5 = hashlib.md5()   ## 实例化hashlib中的md5类
md5.update(so.encode("utf-8"))  ##想把字符串进行算法,要先转换成bytes类型的数据
print(md5.hexdigest(),type(md5.hexdigest()),len(md5.hexdigest()))

执行结果:
2ec318fb8955b68052e8d3e3188c50ae <class 'str'> 32

## 运算出的密文是字符串类型
## 数量为32

# SHA1算法

  1. 40位的算法程序,每个字符都是一个十六进制
  2. SHA1算法效率比较低,算法也相对比较复杂
  3. 目前使用SHA1算法的人比较少,但是也是有,也是有撞库的存在,只是这种算法的撞库数据库比较少
import hashlib
so = "sakjkh21h3h12jkh3jk1h23jk1"
md5 = hashlib.sha1()   ## 实例化hashlib中的sha1类
md5.update(so.encode("utf-8"))  ##想把字符串进行算法,要先转换成bytes类型的数据
print(md5.hexdigest(),type(md5.hexdigest()),len(md5.hexdigest()))

执行结果:
eb3e29dfdff231ad1483fb0abbd5545f8dd34c87 <class 'str'> 40

## 运算出的密文是字符串类型
## 数量为40

# 总结

在hashlib模块中使用算法的过程都是一样的,只是在实例化算法的时候选择自己想要的算法,接下的操作过程都是差不多的,

可以考虑上面的MD5跟SHA1的运算过程,会发现基本一致

无论是MD5或SHA1或其他的算法都有可能被撞库给破解了,这里就要运用一个安全机制的加密,被称为加盐

# hashlib模块加盐

加盐是什么:在输入的字符进行加密运算是,加上指定的字符一起运算,得到结果安全级别比较高,加的盐,如果盐有一点变化,那么运算出的值也会进行变化

# 静态加盐

import hashlib
so = "sakjkh21h3h12jkh3jk1h23jk1"
md5 = hashlib.md5("这是我要加的盐".encode('utf-8')) ## 注意:加盐的值也必须是bytes类型的数据
md5.update(so.encode("utf-8"))
print(md5.hexdigest())
md5_1 = hashlib.md5()
md5_1.update(so.encode("utf-8"))
print(md5_1.hexdigest())

执行结果:
8bfe8b2beb15b9059dde5f8ba4266555
2ec318fb8955b68052e8d3e3188c50ae

在这里不推荐使用静态加盐,为什么呢,因为如果自己的网站使用静态加盐,那黑客通过某种手段,获取到自己的数据库用户信息,那么黑客就可以通过恶意注册用户,进行注册大量的用户,每个用户的密码都不一样, 注册完,拿到自己注册的用户密码的MD5码,在对拿的数据库用户信息进行匹配,匹上,就可以逆推出网站用户密码,这就是静态加盐的不完善之处

# 动态加盐

通过上次的静态加盐,我也很详细的说明了静态加盐的不足之处

那么动态加盐是怎么个动态法

  1. 比如自己的网站用户注册,用户名正常来说,是不允许相同的
  2. 那这样子,就可以使用,用户自身的用户名来对用户的密码进行动态加盐
  3. 也可以通过其他方式,上面这种方式是比较常用的动态加盐方式
import hashlib
so = "sakjkh21h3h12jkh3jk1h23jk1"
passwd = "江凡"
md5 = hashlib.md5(passwd.encode('utf-8')) ## 注意:加盐的值也必须是bytes类型的数据
md5.update(so.encode("utf-8"))
print(md5.hexdigest())

执行结果:
c25d388a53465fe044c55e3f84468e2a

# 模拟用户登录练习题

要求

  1. 模拟用户登录
  2. 密码需要使用MD5加密
  3. 密码的加盐方式,使用动态用户名加盐
import hashlib
user = input("请输入用户名:")
passwd = input("请输入密码:")
md5 = hashlib.md5(user.encode("utf-8"))
md5.update(passwd.encode("utf-8"))
passwd = md5.hexdigest()
if user == "江凡" and passwd == "78326743f649109417bf9b702f4de79e":
    print("登录成功")
else:
    print("账号或密码错误")
    
执行结果:
登录成功

# 文件的一致性校验

创建二个文件,内容一致

## so1 文件
666
666
666

## so2 文件
666
666
666

进行对这so1跟so2二个文件的校验

import hashlib
md5_so1 = hashlib.md5()
with open("so1","rb") as f:
    md5_so1.update(f.read())
    md5_so1 = md5_so1.hexdigest()

md5_so2 = hashlib.md5()
with open("so2","rb") as f:
    md5_so2.update(f.read())
    md5_so2 = md5_so2.hexdigest()

if md5_so1 == md5_so2:
    print("文件一致")
else:
    print("文件不一致")

print(md5_so1)
print(md5_so2)

执行结果:
文件一致
6183c52d316047c44682121a70853948
6183c52d316047c44682121a70853948